{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, TypeSynonymInstances, FunctionalDependencies #-}
module Data.PairLike where

class PairLike f s v | v -> s, v -> f where
        plfst :: v -> f
        plsnd :: v -> s
        plmakePair :: f -> s -> v
        toPair :: v -> (f,s)
        fromPair :: (f,s) -> v
        toPairLike :: (PairLike f s v1) => v -> v1
        fromPairLike :: (PairLike f s v1) => v -> v1
        plswap :: (PairLike s f v1) => v -> v1
        mapPL :: (PairLike f1 s1 v1) => (f -> f1) -> (s -> s1) -> v -> v1
        plfst = fst . toPair
        plsnd = snd . toPair
        plmakePair x y = fromPair (x,y)
        toPair z = (plfst z,plsnd z)
        fromPair (x,y) = plmakePair x y
        toPairLike = fromPair . toPair
        fromPairLike = toPairLike
        plswap z = plmakePair (plsnd z) (plfst z)
        mapPL ff fs v = plmakePair (ff $ plfst v) (fs $ plsnd v)
        
instance PairLike a b (a,b) where
        toPair = id
        fromPair = id
        
        
data Tst1 = Tst1 Int String deriving Show
instance PairLike Int String Tst1 where
        toPair (Tst1 x y) = (x,y)
        plmakePair x y = Tst1 x y
        
data Tst2 = Tst2 String Int deriving Show
instance PairLike Int String Tst2 where
        toPair (Tst2 y x) = (x,y)
        plmakePair x y = Tst2 y x
        
data Tst3 = Tst3 String Int deriving Show
instance PairLike String Int Tst3 where
        toPair (Tst3 x y) = (x,y)
        plmakePair x y = Tst3 x y